package com.daxue.configtest.utils;



import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.BooleanNode;
import com.fasterxml.jackson.databind.node.DoubleNode;
import com.fasterxml.jackson.databind.node.FloatNode;
import com.fasterxml.jackson.databind.node.IntNode;
import com.fasterxml.jackson.databind.node.LongNode;
import com.fasterxml.jackson.databind.node.NullNode;
import com.fasterxml.jackson.databind.node.TextNode;
import lombok.experimental.UtilityClass;

import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.regex.Pattern;


/**
 * @author daxue0929
 * @date 2023/3/26
 */

@UtilityClass
public class JsonNodeUtils {

    public static final Pattern ARRAY_INDEX_PATTERN = Pattern.compile("^\\d+$");
    public static final Pattern WILDCARD_JSON_FILED_PATTERN = Pattern.compile("^\\w+(\\.\\w+(\\[(_|\\d+)])?)+$");
    public static final Pattern JSON_FILED_PATTERN = Pattern.compile("^\\w+(\\.\\w+(\\[\\d+])?)+$");

    private static final ObjectMapper MAPPER = ObjectMapperUtils.getMapper();

    public static String[] keyStringToKeys(final String keyStr, boolean wildcard) {
        if (Objects.isNull(keyStr)) {
            throw new IllegalArgumentException("Empty json path string.");
        }
        if (wildcard && !WILDCARD_JSON_FILED_PATTERN.matcher("dummy." + keyStr).matches()) {
            throw new IllegalArgumentException("Invalid json path string: " + keyStr);
        }
        if (!wildcard && !JSON_FILED_PATTERN.matcher("dummy." + keyStr.replace("._.", "[_].")).matches()) {
            throw new IllegalArgumentException("Invalid json path string: " + keyStr);
        }
        String[] keys = keyStr.replace("]", "").split("[\\.\\[]");
        for (int i = 0; i < keys.length; i++) {
            if (wildcard && ARRAY_INDEX_PATTERN.matcher(keys[i]).matches()) {
                keys[i] = "_";
            }
        }
        return keys;
    }

    public static JsonNode wrapByKeys(final String[] keys, final JsonNode value) {
        JsonNode current = value;
        List<String> paths = Arrays.asList(keys);
        Collections.reverse(paths);
        for (String path : paths) {
            if (path.equals("_")) {
                current = MAPPER.createArrayNode().add(current);
            } else {
                current = MAPPER.createObjectNode().set(path, current);
            }
        }
        return current;
    }

    public static JsonNode wrapNullByKeys(final String[] keys) {
        return wrapByKeys(keys, NullNode.getInstance());
    }

    public static JsonNode wrapByKeys(final String[] keys, final long value) {
        return wrapByKeys(keys, LongNode.valueOf(value));
    }

    public static JsonNode wrapByKeys(final String[] keys, final int value) {
        return wrapByKeys(keys, IntNode.valueOf(value));
    }

    public static JsonNode wrapByKeys(final String[] keys, final float value) {
        return wrapByKeys(keys, FloatNode.valueOf(value));
    }

    public static JsonNode wrapByKeys(final String[] keys, final double value) {
        return wrapByKeys(keys, DoubleNode.valueOf(value));
    }

    public static JsonNode wrapByKeys(final String[] keys, final boolean value) {
        return wrapByKeys(keys, BooleanNode.valueOf(value));
    }

    public static JsonNode wrapByKeys(final String[] keys, final Long value) {
        if (Objects.isNull(value)) {
            return wrapNullByKeys(keys);
        }
        return wrapByKeys(keys, LongNode.valueOf(value));
    }

    public static JsonNode wrapByKeys(final String[] keys, final Integer value) {
        if (Objects.isNull(value)) {
            return wrapNullByKeys(keys);
        }
        return wrapByKeys(keys, IntNode.valueOf(value));
    }

    public static JsonNode wrapByKeys(final String[] keys, final Float value) {
        if (Objects.isNull(value)) {
            return wrapNullByKeys(keys);
        }
        return wrapByKeys(keys, FloatNode.valueOf(value));
    }

    public static JsonNode wrapByKeys(final String[] keys, final Double value) {
        if (Objects.isNull(value)) {
            return wrapNullByKeys(keys);
        }
        return wrapByKeys(keys, DoubleNode.valueOf(value));
    }

    public static JsonNode wrapByKeys(final String[] keys, final Boolean value) {
        if (Objects.isNull(value)) {
            return wrapNullByKeys(keys);
        }
        return wrapByKeys(keys, BooleanNode.valueOf(value));
    }

    public static JsonNode wrapByKeys(final String[] keys, final String value) {
        if (Objects.isNull(value)) {
            return wrapNullByKeys(keys);
        }
        return wrapByKeys(keys, TextNode.valueOf(value));
    }

    public static JsonNode wrapByKeys(final String keyStr, final JsonNode value) {
        return wrapByKeys(keyStringToKeys(keyStr, true), value);
    }

    public static JsonNode wrapByKeys(final String keyStr, final Long value) {
        return wrapByKeys(keyStringToKeys(keyStr, true), LongNode.valueOf(value));
    }

    public static JsonNode wrapByKeys(final String keyStr, final Integer value) {
        return wrapByKeys(keyStringToKeys(keyStr, true), IntNode.valueOf(value));
    }

    public static JsonNode wrapByKeys(final String keyStr, final Float value) {
        return wrapByKeys(keyStringToKeys(keyStr, true), FloatNode.valueOf(value));
    }

    public static JsonNode wrapByKeys(final String keyStr, final Double value) {
        return wrapByKeys(keyStringToKeys(keyStr, true), DoubleNode.valueOf(value));
    }

    public static JsonNode wrapByKeys(final String keyStr, final Boolean value) {
        return wrapByKeys(keyStringToKeys(keyStr, true), BooleanNode.valueOf(value));
    }

    public static JsonNode wrapByKeys(final String keyStr, final String value) {
        return wrapByKeys(keyStringToKeys(keyStr, true), TextNode.valueOf(value));
    }

}
